#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
原神角色文本下载器
根据human.json文件下载所有角色详细信息并生成CSV汇总表
"""

import requests
import json
import os
import csv
import time
import re
from typing import List, Dict, Any, TypeVar

T = TypeVar('T')

def first_cycle(lst: List[T]) -> List[T]:
    """
    给定一个周期性重复的列表，返回其第一个完整周期（最短不重复子序列）。
    如果列表为空或无法判断周期，则返回原列表。
    """
    n = len(lst)
    if n <= 1:
        return lst.copy()

    # 枚举可能的周期长度 k（1..n//2）
    for k in range(1, n // 2 + 1):
        # 检查后面的元素是否完全重复前 k 个
        if all(lst[i] == lst[i + k] for i in range(n - k)):
            return lst[:k]
    return lst.copy()

# 配置
BASE_URL = "https://act-api-takumi-static.mihoyo.com"
CHARACTER_LIST_FILE = "data/human.json"
TASK_LIST_FILE = "data/task/task.json"
OUTPUT_DIR = "data"
TASK_OUTPUT_DIR = "data/task"
TASK_JSON_OUTPUT_DIR = "data/task/json"
CHARACTER_DETAIL_URL = f"{BASE_URL}/hoyowiki/genshin/wapi/entry_page"
DELAY_BETWEEN_REQUESTS = 1  # 请求间隔(秒)

# 创建输出目录
os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(TASK_JSON_OUTPUT_DIR, exist_ok=True)

def load_character_list() -> List[Dict[str, Any]]:
    """
    从human.json加载角色列表
    返回包含content_id和title的列表
    """
    try:
        with open(CHARACTER_LIST_FILE, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 提取角色信息
        characters = []
        if 'data' in data and 'list' in data['data']:
            for category in data['data']['list']:
                if 'list' in category:
                    for item in category['list']:
                        if 'content_id' in item and 'title' in item:
                            print(f"find {item['title']} {item['content_id']}")
                            characters.append({
                                'content_id': item['content_id'],
                                'title': item['title'],
                                'summary': item.get('summary', ''),
                                'icon': item.get('icon', '')
                            })
        
        print(f"从{CHARACTER_LIST_FILE}中解析出{len(characters)}个角色")
        return characters
        
    except FileNotFoundError:
        print(f"错误: 找不到文件 {CHARACTER_LIST_FILE}")
        return []
    except json.JSONDecodeError as e:
        print(f"错误: JSON解析失败 - {e}")
        return []

def load_task_list() -> List[Dict[str, Any]]:
    """
    从task.json加载任务列表
    返回包含content_id、title和summary的列表
    """
    try:
        with open(TASK_LIST_FILE, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 提取任务信息
        tasks = []
        if 'data' in data and 'list' in data['data']:
            for category in data['data']['list']:
                if 'list' in category:
                    for item in category['list']:
                        if 'content_id' in item and 'title' in item:
                            # 解析ext字段获取更多信息
                            ext_info = {}
                            if 'ext' in item and item['ext']:
                                try:
                                    ext_data = json.loads(item['ext'])
                                    if 'c_43' in ext_data and 'filter' in ext_data['c_43']:
                                        filter_text = ext_data['c_43']['filter'].get('text', '')
                                        ext_info['filter'] = filter_text
                                except:
                                    pass
                            
                            tasks.append({
                                'content_id': item['content_id'],
                                'title': item['title'],
                                'summary': item.get('summary', ''),
                                'icon': item.get('icon', ''),
                                'ext': ext_info
                            })
        
        print(f"从{TASK_LIST_FILE}中解析出{len(tasks)}个任务")
        return tasks
        
    except FileNotFoundError:
        print(f"错误: 找不到文件 {TASK_LIST_FILE}")
        return []
    except json.JSONDecodeError as e:
        print(f"错误: JSON解析失败 - {e}")
        return []

def download_wiki_page_detail(content_id: int, title: str) -> Dict[str, Any]:
    """
    下载单个角色的详细信息
    
    Args:
        content_id: 角色内容ID
        title: 角色名称
        
    Returns:
        角色详细信息字典
    """
    params = {
        'app_sn': 'ys_obc',
        'entry_page_id': content_id,
        'lang': 'zh-cn'
    }
    
    try:
        response = requests.get(CHARACTER_DETAIL_URL, params=params, timeout=30)
        response.raise_for_status()
        
        data = response.json()
        
        # 保存原始JSON文件
        filename = f"{content_id}-{title}.json"
        safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
        filepath = os.path.join(OUTPUT_DIR, safe_filename)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        print(f"已下载: {safe_filename}")
        return data
        
    except requests.RequestException as e:
        print(f"下载失败 {title} (ID: {content_id}): {e}")
        return {}
    except Exception as e:
        print(f"处理失败 {title} (ID: {content_id}): {e}")
        return {}

def merge_repeat_lines(text: str) -> str:
    """
    把输入的多行字符串每 2 行合并为一行，中间用一个空格分隔。
    如果总行数是奇数，最后一行保持原样。
    """
    merged = []
    for line in text.splitlines():
        if line.strip() == '日语':
            break
        elif line.strip() != '汉语':
            merged.append(line.strip())
    return "\n".join(merged)

def format_line(text: str) -> str:
    merged = []
    lines = text.splitlines()
    for i in range(0, len(lines)):
        if lines[i].startswith('天赋'):
            merged.append('\n')
        elif i>0 and lines[i-1].startswith('天赋'):
            merged.append( lines[i-1]+'：' + lines[i] )
        else:
            merged.append(lines[i])
    return "\n".join(merged)
    # # (?s) 让 . 匹配换行；(?m) 让 ^ 匹配行首
    # pattern = r'(?sm)'+aaa+r'.*?^\s*'+bbb
    # return re.sub(pattern, bbb, text).strip()

def extract_text_from_json(data: Dict[str, Any]) -> Dict[str, str]:
    """
    从角色JSON数据中提取文本内容
    
    Args:
        data: 角色详细信息JSON数据
        
    Returns:
        包含提取文本的字典
    """
    result = {
        'name': '',
        'title': '',
        'description': '',
        'lore': '',
        'personality': '',
        'appearance': '',
        'all_text': '',
        'basic_info': '',
        'talents': '',
        'constellations': '',
        'character_details': '',
        'voice_lines': '',
        'character_stories': ''
    }
    
    try:
        if 'data' not in data:
            return result
            
        page_data = data['data']
        
        # 提取基础信息
        if 'page' in page_data:
            page = page_data['page']
            result['name'] = page.get('name', '')
            result['title'] = page.get('title', '')
            result['description'] = page.get('desc', '')
        
        # 提取模块内容
        if 'modules' in page:
            basic_info_parts = []
            talents_parts = []
            constellations_parts = []
            details_parts = []
            voice_parts = []
            others_comment_parts = []
            stories_parts = []
            mails_parts = []

            story_cnt = 0

            for module in page['modules']:
                module_title = module.get('name', '')
                    
                if 'components' in module:
                    for component in module['components']:
                        # 提取文本内容
                        if 'data' in component:
                            data_content = json.loads(component['data'])
                            
                            # 处理不同类型的数据
                            text_content = extract_text_from_component(data_content)
                            # print(text_content)

                            # 根据模块标题分类内容
                            if '基础信息' in module_title:
                                basic_info_parts.append(text_content)
                            elif '天赋' in module_title:
                                talents_parts.append(format_line(text_content)) # , '详细属性', '天赋')
                            elif '命之座' in module_title:
                                constellations_parts.append(text_content)
                            elif '角色详细' in module_title or '更多描述' in module_title:
                                details_parts.append(text_content)
                            elif '配音' in module_title:
                                voice_parts.append(merge_repeat_lines(text_content))
                            elif '语音' in module_title:
                                others_comment_parts.append(text_content)
                            elif '生日邮件' in module_title:
                                mails_parts.append(text_content)
                            elif '角色故事' in module_title:
                                stories_parts.append(text_content)
                                story_cnt += 1
                            elif story_cnt > 0:
                                stories_parts.append(text_content)
                                story_cnt += 1
                                if story_cnt >= 7:
                                    break
            # 填充结果
            result['basic_info'] = '\n\n'.join(filter(None, basic_info_parts))
            result['talents'] = '\n\n'.join(filter(None, talents_parts))
            result['constellations'] = '\n\n'.join(filter(None, constellations_parts))
            result['character_details'] = '\n\n'.join(filter(None, details_parts))
            result['voice_lines'] = '\n\n'.join(filter(None, voice_parts))
            result['others_comments'] = '\n\n'.join(filter(None, others_comment_parts))
            result['character_stories'] = '\n\n'.join(filter(None, stories_parts))
            result['mails'] = '\n\n'.join(filter(None, mails_parts))
            
            # 清理HTML标签
            for key in result:
                if isinstance(result[key], str):
                    result[key] = re.sub(r'<[^>]+>', '', result[key])
                    result[key] = re.sub(r'\n{3,}', '\n\n', result[key]).strip()
            
    except Exception as e:
        print(f"提取文本时出错: {e}")
    
    return result

def extract_text_from_component(data_content) -> str:
    """
    从组件数据中提取纯文本内容
    """
    text_parts = []
    
    def clean_html_tags(text):
        """清理HTML标签但保留<p>标签的换行"""
        if not text:
            return ""
        
        # 将<p>标签替换为换行符
        text = re.sub(r'\s*<\s*p\s*[^>]*\s*>\s*', '\n', text, flags=re.IGNORECASE)
        text = re.sub(r'\s*<\s*/\s*p\s*>\s*', '\n', text, flags=re.IGNORECASE)
        
        # 将<br>标签替换为换行符
        text = re.sub(r'\s*<\s*br\s*/?\s*>\s*', '\n', text, flags=re.IGNORECASE)
        
        # 移除其他HTML标签
        text = re.sub(r'<[^>]+>', '', text)
        
        # 清理多余的空格和换行，但保留段落结构
        text = re.sub(r'\n{3,}', '\n\n', text)  # 多个换行合并为两个
        text = re.sub(r'[ \t]+', ' ', text)  # 合并连续空格
        text = text.strip()
        
        return text
    
    def _extract_recursive(obj, depth=0):
        if depth > 10:  # 防止无限递归
            return
            
        if isinstance(obj, dict):
            # 提取常见文本字段
            if 'name' in obj and 'content' in obj:
                text_parts.append('【'+clean_html_tags(obj['name'])+'】：“' + clean_html_tags(obj['content'])+'”')
            for key in ['tab_name', 'title', 'desc', 'text', 'richtext', 'summary']:
                if key in obj and obj[key] and isinstance(obj[key], str):
                    text_parts.append(clean_html_tags(obj[key]))
            
            # 递归处理子对象
            for key in obj:
                value = obj[key]
                if isinstance(value, (dict, list)) and (key != 'attr' or 'tab_name' not in obj):
                    _extract_recursive(value, depth + 1)
                    
        elif isinstance(obj, list):
            for item in obj:
                if isinstance(item, (dict, list)):
                    _extract_recursive(item, depth + 1)
                elif isinstance(item, str) and item.strip():
                    text_parts.append(clean_html_tags(item.strip()))
    
    if isinstance(data_content, dict) and 'rich_text' in data_content:
        return clean_html_tags(data_content['rich_text'])
    else:
        _extract_recursive(data_content)
        return '\n'.join(filter(None, text_parts))

def create_character_csv(characters: List[Dict[str, Any]]) -> None:
    """
    创建包含所有角色信息的CSV文件
    
    Args:
        characters: 角色列表
    """
    csv_file = os.path.join(OUTPUT_DIR, "human.csv")
    
    # CSV表头
    headers = [
        'content_id',
        'name',
        'title',
        'description',
        'lore',
        'personality',
        'appearance',
        'all_text',
        'raw_filename'
    ]
    
    with open(csv_file, 'w', encoding='utf-8-sig', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=headers)
        writer.writeheader()
        
        for character in characters:
            content_id = character['content_id']
            title = character['title']
            
            # 构建文件名
            filename = f"{content_id}-{title}.json"
            safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
            filepath = os.path.join(OUTPUT_DIR, safe_filename)
            
            # 如果文件存在，提取内容
            if os.path.exists(filepath):
                try:
                    with open(filepath, 'r', encoding='utf-8') as json_file:
                        data = json.load(json_file)
                    
                    text_data = extract_text_from_json(data)
                    
                    # 写入CSV行
                    row = {
                        'content_id': content_id,
                        'name': text_data.get('name', title),
                        'title': text_data.get('title', ''),
                        'description': text_data.get('description', ''),
                        'lore': text_data.get('lore', ''),
                        'personality': text_data.get('personality', ''),
                        'appearance': text_data.get('appearance', ''),
                        'all_text': text_data.get('all_text', ''),
                        'raw_filename': safe_filename
                    }
                    
                    writer.writerow(row)
                    
                except Exception as e:
                    print(f"处理CSV行失败 {title}: {e}")
                    # 写入空行
                    writer.writerow({
                        'content_id': content_id,
                        'name': title,
                        'title': '',
                        'description': '',
                        'lore': '',
                        'personality': '',
                        'appearance': '',
                        'all_text': '',
                        'raw_filename': safe_filename
                    })
    
    print(f"CSV文件已创建: {csv_file}")

def create_markdown_files(characters: List[Dict[str, Any]]) -> None:
    """
    为每个角色创建markdown文档
    
    Args:
        characters: 角色列表
    """
    markdown_dir = os.path.join(OUTPUT_DIR, "markdown")
    os.makedirs(markdown_dir, exist_ok=True)
    
    for character in characters:
        content_id = character['content_id']
        title = character['title']
        
        # 构建文件名
        filename = f"{content_id}-{title}.json"
        safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
        filepath = os.path.join(OUTPUT_DIR, safe_filename)
        
        # 如果JSON文件存在，生成markdown
        if os.path.exists(filepath) and '预告' not in title:
            try:
                with open(filepath, 'r', encoding='utf-8') as json_file:
                    data = json.load(json_file)
                
                text_data = extract_text_from_json(data)
                
                # 创建markdown内容
                markdown_content = generate_markdown_content(character, text_data)
                
                # 保存markdown文件
                md_filename = f"{content_id}-{title}.md"
                safe_md_filename = "".join(c for c in md_filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
                md_filepath = os.path.join(markdown_dir, safe_md_filename)
                
                with open(md_filepath, 'w', encoding='utf-8') as f:
                    f.write(markdown_content)
                
                print(f"已生成markdown: {safe_md_filename}")
                
            except Exception as e:
                print(f"生成markdown失败 {title}: {e}")

def generate_markdown_content(character: Dict[str, Any], text_data: Dict[str, str]) -> str:
    """
    生成角色的markdown文档内容
    
    Args:
        character: 角色基础信息
        text_data: 提取的文本内容
        
    Returns:
        markdown格式的字符串
    """
    title = character['title']
    
    markdown_parts = [
        f"# {title}",
        "",
        "## 基础信息",
        text_data.get('basic_info', '暂无基础信息'),
        "",
        "## 天赋",
        text_data.get('talents', '暂无天赋信息'),
        "",
        "## 命之座",
        text_data.get('constellations', '暂无命之座信息'),
        "",
        "## 角色详细",
        text_data.get('character_details', '暂无详细描述'),
        "",
        "## 角色台词",
        text_data.get('voice_lines', '暂无台词信息'),
        "",
        "## 他人评价",
        text_data.get('others_comments', '暂无评价信息'),
        "",
        "## 角色故事",
        text_data.get('character_stories', '暂无故事信息'),
        "",
        "## 生日邮件",
        text_data.get('mail', '暂无生日邮件'),
        ""
    ]
    
    return '\n'.join(markdown_parts)

def download_task_detail(content_id: int, title: str) -> Dict[str, Any]:
    """
    下载单个任务的详细信息
    
    Args:
        content_id: 任务内容ID
        title: 任务名称
        
    Returns:
        任务详细信息字典
    """
    params = {
        'app_sn': 'ys_obc',
        'entry_page_id': content_id,
        'lang': 'zh-cn'
    }
    
    try:
        response = requests.get(CHARACTER_DETAIL_URL, params=params, timeout=30)
        response.raise_for_status()
        
        data = response.json()
        
        # 保存原始JSON文件到任务目录
        filename = f"{content_id}-{title}.json"
        safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
        filepath = os.path.join(TASK_JSON_OUTPUT_DIR, safe_filename)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        print(f"已下载任务: {safe_filename}")
        return data
        
    except requests.RequestException as e:
        print(f"下载任务失败 {title} (ID: {content_id}): {e}")
        return {}
    except Exception as e:
        print(f"处理任务失败 {title} (ID: {content_id}): {e}")
        return {}

def extract_task_text_from_json(data: Dict[str, Any]) -> Dict[str, str]:
    """
    从任务JSON数据中提取文本内容
    
    Args:
        data: 任务详细信息JSON数据
        
    Returns:
        包含提取文本的字典
    """
    
    taskInfo = {
        'name': '',
        'title': '',
        'description': '',
        'category': [],
        'tasks': []
    }
    try:
        if 'data' not in data:
            return result
            
        # 提取基础信息
        pageTaskGroup = []
        page_data = data['data']
        if 'page' in page_data:
            page = page_data['page']
            taskInfo['name'] = page.get('name', '')
            taskInfo['title'] = page.get('title', '')
            taskInfo['description'] = page.get('desc', '')
            # 提取页面layout信息
            if 'template_layout' in page and 'tab' in page['template_layout']:
                for tabData in page['template_layout']['tab']:
                    if 'module_group' in tabData and isinstance(tabData['module_group'], list):
                        for mgrp in tabData['module_group']:
                            moduleIds = []
                            if 'module' in mgrp and isinstance(mgrp['module'], list):
                                for m in mgrp['module']:
                                    moduleIds.append(m['id'])
                            pageTaskGroup.append(moduleIds)
        
        moduleContent = {}
        # 提取模块内容
        if 'modules' in page:
            for module in page['modules']:
                module_title = module.get('name', '')
                module_id = module.get('id', '')
                if 'components' in module:
                    for component in module['components']:
                        component_id = component.get('component_id', '')
                        if 'data' in component:
                            data_content = json.loads(component['data'])
                            text_content = extract_text_from_task_component(data_content, component_id)
                            moduleContent[module_id] = (module_title, component_id, text_content)
        
        tasks = []
        for taskMids in pageTaskGroup:
            overview_parts = []
            dialogue_parts = []
            process_parts = []
            rewards_parts = []
            taskObj = {}
            for module_id in taskMids:
                if module_id in moduleContent:
                    module_title, component_id, text_content = moduleContent[module_id]
                    # 根据组件ID和模块标题分类内容
                    if component_id == 'base_info':
                        taskObj['name'] = module_title
                        overview_parts.append(text_content)
                    elif component_id == 'rich_base_info':
                        overview_parts.append(text_content)
                    elif component_id == 'interactive_dialogue':
                        dialogue_parts.append(text_content)
                    elif component_id == 'collapse_panel':
                        if '任务概述' in module_title:
                            overview_parts.append(text_content)
                        elif '剧情对话' in module_title or '对话' in module_title:
                            dialogue_parts.append(text_content)
                        elif '任务过程' in module_title or '流程' in module_title:
                            process_parts.append(text_content)
                        elif '任务奖励' in module_title or '奖励' in module_title:
                            rewards_parts.append(text_content)
            
            # 填充结果
            taskObj['overview'] = '\n\n'.join(filter(None, overview_parts))
            taskObj['dialogue'] = '\n\n'.join(filter(None, dialogue_parts))
            taskObj['process'] = '\n\n'.join(filter(None, process_parts))
            taskObj['rewards'] = '\n\n'.join(filter(None, rewards_parts))
            
            # 清理HTML标签
            for key in taskObj:
                if isinstance(taskObj[key], str):
                    taskObj[key] = re.sub(r'<[^>]+>', '', taskObj[key])
                    taskObj[key] = re.sub(r'\n{3,}', '\n\n', taskObj[key]).strip()
            tasks.append(taskObj)
    except Exception as e:
        print(f"提取任务文本时出错: {e}")
    
    taskInfo['tasks'] = tasks
    return taskInfo

def extract_text_from_task_component(data_content, component_id: str) -> str:
    """
    从任务组件数据中提取纯文本内容
    """
    text_parts = []
    
    def clean_html_tags(text):
        """清理HTML标签但保留段落结构"""
        if not text:
            return ""
        
        # 将<p>标签替换为换行符
        text = re.sub(r'\s*<\s*p\s*[^>]*\s*>\s*', '\n', text, flags=re.IGNORECASE)
        text = re.sub(r'\s*<\s*/\s*p\s*>\s*', '\n', text, flags=re.IGNORECASE)
        
        # 将<br>标签替换为换行符
        text = re.sub(r'\s*<\s*br\s*/?\s*>\s*', '\n', text, flags=re.IGNORECASE)
        
        # 将<li>标签替换为带项目符号的文本
        text = re.sub(r'\s*<\s*li\s*[^>]*\s*>\s*', '\n• ', text, flags=re.IGNORECASE)
        text = re.sub(r'\s*<\s*/\s*li\s*>\s*', '\n', text, flags=re.IGNORECASE)
        
        # 将<ul>和<ol>标签替换为换行符
        text = re.sub(r'\s*<\s*(ul|ol)\s*[^>]*\s*>\s*', '\n', text, flags=re.IGNORECASE)
        text = re.sub(r'\s*<\s*/\s*(ul|ol)\s*>\s*', '\n', text, flags=re.IGNORECASE)

        # 将<strong>标签替换为加粗文本
        text = re.sub(r'\s*<\s*strong\s*[^>]*\s*>\s*', '【', text, flags=re.IGNORECASE)
        text = re.sub(r'\s*<\s*/\s*strong\s*>\s*', '】', text, flags=re.IGNORECASE)

        # 将<a>标签替换为加粗文本
        text = re.sub(r'\s*<\s*a\s*[^>]*\s*>\s*', '【', text, flags=re.IGNORECASE)
        text = re.sub(r'\s*<\s*/\s*a\s*>\s*', '】', text, flags=re.IGNORECASE)
        
        # 移除其他HTML标签
        text = re.sub(r'<[^>]+>', '', text)
        
        # 清理多余的空格和换行，但保留段落结构
        text = re.sub(r'\n{3,}', '\n\n', text)  # 多个换行合并为两个
        text = re.sub(r'[ \t]+', ' ', text)  # 合并连续空格
        text = text.strip()
        
        return text
    
    def _extract_interactive_dialogue(dialogue_data):
        """处理交互式对话，按正确顺序提取"""
        if not isinstance(dialogue_data, dict):
            return
            
        # 获取对话数据 - 支持两种数据结构
        root_id = dialogue_data.get('root_id', [])
        contents = dialogue_data.get('contents', {})
        child_ids = dialogue_data.get('child_ids', {})
        
        # 处理空数据情况
        if not contents:
            return
            
        # 使用BFS遍历对话树，按层级顺序提取
        from collections import deque
        
        siblingCnts = {}
        visited = set()
        queue = deque([root_id])
        
        while queue:
            node_id = queue.popleft()
            
            if node_id in visited or node_id not in contents:
                continue
                
            visited.add(node_id)
            content = contents[node_id]
            
            # 处理选项
            hasOption = False
            if 'option' in content and content['option'] and content['option'].strip():
                option_text = clean_html_tags(content['option']).strip()
                if option_text:
                    text_parts.append(f"**{option_text}**")
                    hasOption = True
            
            # 处理对话内容
            if 'dialogue' in content and content['dialogue'] and content['dialogue'].strip():
                dialogue_content = clean_html_tags(content['dialogue']).strip()
                if dialogue_content:
                    if hasOption and node_id in siblingCnts and siblingCnts[node_id]>1:
                        text_parts.append('- ' + '\n- '.join(dialogue_content.splitlines()))
                    else:
                        text_parts.append(dialogue_content)
            
            
            # 将子节点加入队列，保持对话顺序
            if node_id in child_ids and isinstance(child_ids[node_id], list):
                child_id_list = []
                for child_id in child_ids[node_id]:
                    if child_id not in visited and child_id in contents:
                        child_id_list.append(child_id)
                if len(child_id_list) > 1:
                    text_parts.append(f"【对话选项x{len(child_id_list)}】")
                for child_id in child_id_list: siblingCnts[child_id] = len(child_id_list)
                queue.extend(child_id_list)
    
    def _extract_recursive(obj, depth=0):
        if depth > 10:  # 防止无限递归
            return
            
        if isinstance(obj, dict):
            # 处理交互式对话
            if component_id == 'interactive_dialogue':
                if 'list' in obj:
                    for item in obj['list']:
                        if isinstance(item, dict) and 'root_id' in item and 'contents' in item and 'child_ids' in item:
                            _extract_interactive_dialogue(item)
                            return
                elif 'root_id' in obj and 'contents' in obj and 'child_ids' in obj:
                    _extract_interactive_dialogue(obj)
                    return
            
            # 任务概述信息
            if 'list' in obj and isinstance(obj['list'], list):
                for item in obj['list']:
                    if isinstance(item, dict) and 'key' in item and 'value' in item:
                        key = item['key']
                        value = item['value']
                        if isinstance(value, list):
                            value = ' '.join(str(v) for v in value)
                        text_parts.append(f"{key}: {value}")
            
            # 提取富文本内容
            if 'rich_text' in obj and obj['rich_text']:
                text_parts.append(clean_html_tags(obj['rich_text']))
            
            # 递归处理子对象
            for key in obj:
                value = obj[key]
                if key in ['list', 'rich_text']: continue
                if isinstance(value, (dict, list)):
                    _extract_recursive(value, depth + 1)
                elif isinstance(value, str) and value.strip():
                    text_parts.append(clean_html_tags(value.strip()))
                    
        elif isinstance(obj, list):
            for item in obj:
                if isinstance(item, (dict, list)):
                    _extract_recursive(item, depth + 1)
                elif isinstance(item, str) and item.strip():
                    text_parts.append(clean_html_tags(item.strip()))
    
    _extract_recursive(data_content)
    return '\n'.join(filter(None, text_parts))

def create_task_markdown_files(tasks: List[Dict[str, Any]]) -> None:
    """
    为每个任务创建markdown文档
    
    Args:
        tasks: 任务列表
    """
    markdown_dir = TASK_OUTPUT_DIR
    os.makedirs(markdown_dir, exist_ok=True)

    def taskCategory(task):
        ext_info = ""
        if 'ext' in task and task['ext'] and 'filter' in task['ext']:
            filter_text = task['ext']['filter']
            if '魔神' in filter_text:
                return "主线"
            elif '限时' in filter_text:
                return "活动"
            elif '传说' in filter_text:
                return "传说"
            elif '世界' in filter_text:
                return "世界"
        return "其他"
    
    for task in tasks:
        content_id = task['content_id']
        title = task['title']
        
        # 构建文件名
        filename = f"{content_id}-{title}.json"
        safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
        filepath = os.path.join(TASK_JSON_OUTPUT_DIR, safe_filename)
        
        # 如果JSON文件存在，生成markdown
        if os.path.exists(filepath):
            try:
                with open(filepath, 'r', encoding='utf-8') as json_file:
                    data = json.load(json_file)
                
                text_data = extract_task_text_from_json(data)
                
                # 创建markdown内容
                markdown_content = generate_task_markdown_content(task, text_data)
                
                category = taskCategory(task)
                # 保存markdown文件
                if title.startswith('邀约事件') and category=='传说':
                    md_filename = f"邀约-{content_id}-{title[4:]}.md"
                else:
                    md_filename = f"{category}-{content_id}-{title}.md"
                safe_md_filename = "".join(c for c in md_filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
                md_filepath = os.path.join(markdown_dir, safe_md_filename)
                
                with open(md_filepath, 'w', encoding='utf-8') as f:
                    f.write(markdown_content)
                
                print(f"已生成任务markdown: {safe_md_filename}")
                
            except Exception as e:
                print(f"生成任务markdown失败 {title}: {e}")

def generate_task_markdown_content(task: Dict[str, Any], text_data: Dict[str, str]) -> str:
    """
    生成任务的markdown文档内容
    
    Args:
        task: 任务基础信息
        text_data: 提取的文本内容
        
    Returns:
        markdown格式的字符串
    """
    title = task['title']
    summary = task.get('summary', '')
    
    # 解析扩展信息
    ext_info = ""
    if 'ext' in task and task['ext'] and 'filter' in task['ext']:
        filter_text = task['ext']['filter']
        ext_info = f"任务分类: {filter_text}"
    
    markdown_parts = [
        f"# {title}",
        "",
    ]
    
    if summary:
        markdown_parts.extend([
            f"**{summary}**",
            ""
        ])
    
    if ext_info:
        markdown_parts.extend([
            f"*{ext_info}*",
            ""
        ])
    
    for task in text_data.get('tasks', []):
        markdown_parts.extend([
            "",
            "## 【任务】"+task['name'],
            "",
            "### 任务概述",
            task.get('overview', '暂无任务概述信息'),
            "",
            "### 任务过程",
            task.get('process', '暂无任务过程信息'),
            "",
            "### 剧情对话",
            task.get('dialogue', '暂无剧情对话信息'),
            "",
            "### 任务奖励",
            task.get('rewards', '暂无任务奖励信息'),
            ""
        ])
    
    return '\n'.join(markdown_parts)

def main():
    """主函数"""
    print("开始处理原神角色文本...")
    
    # 1. 加载角色列表
    characters = load_character_list()
    if not characters:
        print("没有找到角色信息，程序退出")
        return
    
    # 2. 下载角色详情
    print("\n开始下载角色详情...")
    downloaded_count = 0
    failed_count = 0
    
    for character in characters:
        content_id = character['content_id']
        title = character['title']
        
        # 检查文件是否已存在
        filename = f"{content_id}-{title}.json"
        safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
        filepath = os.path.join(OUTPUT_DIR, safe_filename)
        
        if os.path.exists(filepath):
            print(f"跳过已存在的文件: {safe_filename}")
            downloaded_count += 1
            continue
        
        # 下载角色详情
        data = download_wiki_page_detail(content_id, title)
        if data:
            downloaded_count += 1
        else:
            failed_count += 1
        
        # 延迟避免请求过快
        time.sleep(DELAY_BETWEEN_REQUESTS)
    
    print(f"\n下载完成: 成功 {downloaded_count}, 失败 {failed_count}")
    
    # 3. 创建CSV汇总表
    # print("\n开始创建CSV汇总表...")
    # create_character_csv(characters)
    
    # 4. 生成markdown文档
    print("\n开始生成markdown文档...")
    create_markdown_files(characters)
    
    print("\n任务全部完成!")
    print(f"文件保存目录: {os.path.abspath(OUTPUT_DIR)}")
    print(f"markdown文档目录: {os.path.join(os.path.abspath(OUTPUT_DIR), 'markdown')}")

def process_tasks():
    """处理任务数据的主函数"""
    print("开始处理原神任务文本...")
    
    # 1. 加载任务列表
    tasks = load_task_list()
    if not tasks:
        print("没有找到任务信息，程序退出")
        return
    
    # 2. 下载任务详情
    print("\n开始下载任务详情...")
    downloaded_count = 0
    failed_count = 0
    
    for task in tasks:
        content_id = task['content_id']
        title = task['title']
        
        # 检查文件是否已存在
        filename = f"{content_id}-{title}.json"
        safe_filename = "".join(c for c in filename if c.isalnum() or c in (' ', '-', '_', '.')).rstrip()
        filepath = os.path.join(TASK_JSON_OUTPUT_DIR, safe_filename)
        
        if os.path.exists(filepath):
            print(f"跳过已存在的文件: {safe_filename}")
            downloaded_count += 1
            continue
        
        # 下载任务详情
        data = download_task_detail(content_id, title)
        if data:
            downloaded_count += 1
        else:
            failed_count += 1
        
        # 延迟避免请求过快
        time.sleep(DELAY_BETWEEN_REQUESTS)
    
    print(f"\n任务下载完成: 成功 {downloaded_count}, 失败 {failed_count}")
    
    # 3. 生成任务markdown文档
    print("\n开始生成任务markdown文档...")
    create_task_markdown_files(tasks)
    
    print("\n任务处理全部完成!")
    print(f"任务JSON文件目录: {os.path.abspath(TASK_JSON_OUTPUT_DIR)}")
    print(f"任务markdown文档目录: {os.path.abspath(TASK_OUTPUT_DIR)}")

if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == "tasks":
        process_tasks()
    if len(sys.argv) > 1 and sys.argv[1] == "characters":
        main()
    else:
        process_tasks()